package com.dgz.system.service.exam.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dgz.common.core.constants.Constants;
import com.dgz.common.core.enums.ResultCode;
import com.dgz.common.security.exception.ServiceException;
import com.dgz.system.domain.exam.Exam;
import com.dgz.system.domain.exam.ExamQuestion;
import com.dgz.system.domain.exam.dto.ExamAddDto;
import com.dgz.system.domain.exam.dto.ExamEditDto;
import com.dgz.system.domain.exam.dto.ExamQueryDto;
import com.dgz.system.domain.exam.dto.ExamQuestionAddDto;
import com.dgz.system.domain.exam.vo.ExamDetailVO;
import com.dgz.system.domain.exam.vo.ExamVO;
import com.dgz.system.domain.question.Question;
import com.dgz.system.domain.question.vo.QuestionVO;
import com.dgz.system.manager.ExamCacheManager;
import com.dgz.system.mapper.exam.ExamMapper;
import com.dgz.system.mapper.exam.ExamQuestionMapper;
import com.dgz.system.mapper.question.QuestionMapper;
import com.dgz.system.service.exam.IExamService;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: DongGuoZhen
 * @Date: 2025/04/06/16:11
 * @Description:
 */
@Service
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper,ExamQuestion> implements IExamService {


    @Autowired
    private ExamMapper examMapper;
    
    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private ExamQuestionMapper examQuestionMapper;

    @Autowired
    private ExamCacheManager examCacheManager;

    @Override
    public List<ExamVO> list(ExamQueryDto examQueryDto) {
        PageHelper.startPage(examQueryDto.getPageNum(), examQueryDto.getPageSize());
        return examMapper.selectExamList(examQueryDto);
    }

    @Override
    public String add(ExamAddDto examAddDto) {
        checkExamSavaParams(examAddDto,null);
        Exam exam = new Exam();
        BeanUtil.copyProperties(examAddDto, exam);
        examMapper.insert(exam);
        return exam.getExamId().toString();
    }

    @Override
    public boolean questionAdd(ExamQuestionAddDto examQuestionAddDto) {
        Exam exam = getExam(examQuestionAddDto.getExamId());
        checkExam(exam);
        if(Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        Set<Long> questionIdSet = examQuestionAddDto.getQuestionIdSet();
        if(CollectionUtil.isEmpty(questionIdSet)) {
            return true;
        }
        return saveExamQuestion(questionIdSet, exam);

    }

    @Override
    public ExamDetailVO detail(Long examId) {
        ExamDetailVO examDetailVO = new ExamDetailVO();
        Exam exam = getExam(examId);
        BeanUtil.copyProperties(exam, examDetailVO);
        List<ExamQuestion> examQuestionList = examQuestionMapper
                .selectList(new LambdaQueryWrapper<ExamQuestion>()
                        .select(ExamQuestion::getQuestionId)
                        .eq(ExamQuestion::getExamId, examId)
                        .orderByAsc(ExamQuestion::getQuestionOrder));
        if(CollectionUtil.isEmpty(examQuestionList)) {
            return examDetailVO;
        }
        List<Long> questionIdList = examQuestionList.stream().map(ExamQuestion::getQuestionId).toList();
        List<Question> questions = questionMapper.selectList(new LambdaQueryWrapper<Question>()
                .select(Question::getQuestionId,Question::getTitle,Question::getDifficulty)
                .in(Question::getQuestionId, questionIdList));
//        List<QuestionVO> questionVOList = new ArrayList<>();
        List<QuestionVO> questionVOList = BeanUtil.copyToList(questions, QuestionVO.class);
        examDetailVO.setExamQuestionList(questionVOList);
        return examDetailVO;

    }

    @Override
    public int edit(ExamEditDto examEditDto) {
        Exam exam = getExam(examEditDto.getExamId());
        if(Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        checkExam(exam);
        checkExamSavaParams(examEditDto,examEditDto.getExamId());
        exam.setTitle(examEditDto.getTitle());
        exam.setStartTime(examEditDto.getStartTime());
        exam.setEndTime(examEditDto.getEndTime());
        return examMapper.updateById(exam);
    }

    @Override
    public int questionDelete(Long examId, Long questionId) {
        Exam exam = getExam(examId);
        if(Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        checkExam(exam);
        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId)
                .eq(ExamQuestion::getQuestionId, questionId));
    }

    @Override
    public int delete(Long examId) {
        Exam exam = getExam(examId);
        checkExam(exam);
        examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));
        return examMapper.deleteById(examId);
    }

    @Override
    public int publish(Long examId) {
        Exam exam = getExam(examId);
        if(exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }
        Long examQuestionCount = examQuestionMapper.selectCount(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));
        if(examQuestionCount == null || examQuestionCount <= 0) {
            throw new ServiceException(ResultCode.EXAM_NOT_HAS_QUESTION);
        }
        exam.setStatus(Constants.TRUE);
        // 将发布的竞赛发布到redis中
        examCacheManager.addCache(exam);
        return examMapper.updateById(exam);
    }

    @Override
    public int cancelPublish(Long examId) {
        Exam exam = getExam(examId);
        checkExam(exam);
        if(exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }
        exam.setStatus(Constants.FALSE);
        examCacheManager.deleteCache(exam.getExamId());
        return examMapper.updateById(exam);
    }

    private boolean saveExamQuestion(Set<Long> questionIdSet, Exam exam) {
        int num = 1;
        List<Question> questionList = questionMapper.selectBatchIds(questionIdSet);
        if(CollectionUtil.isEmpty(questionList) || questionList.size() < questionIdSet.size()) {
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXISTS);
        }
        List<ExamQuestion> examQuestionList = new ArrayList<>();
        for (Long questionId : questionIdSet) {
            ExamQuestion examQuestion = new ExamQuestion();
            examQuestion.setQuestionId(questionId);
            examQuestion.setExamId(exam.getExamId());
            examQuestion.setQuestionOrder(num++);
            examQuestionList.add(examQuestion);
        }
        return saveBatch(examQuestionList);
    }

    private Exam getExam(Long examId) {
        Exam exam = examMapper.selectById(examId);
        if(exam == null) {
            throw new ServiceException(ResultCode.EXAM_NOT_EXISTS);
        }
        return exam;
    }

    // 竞赛校验
    private void checkExamSavaParams(ExamAddDto examSavaDto,Long examId) {
        List<Exam> exams = examMapper
                .selectList(new LambdaQueryWrapper<Exam>()
                        .eq(Exam::getTitle, examSavaDto.getTitle())
                        .ne(examId != null,Exam::getExamId,examId));
        if(CollectionUtil.isNotEmpty(exams)) {
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }
//        isBefore()?  before : after
        if(examSavaDto.getStartTime().isBefore(LocalDateTime.now())) {
//            开始时间不能早于当前时间
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);
        }
        if(examSavaDto.getEndTime().isBefore(examSavaDto.getStartTime())) {
//            结束时间不能早于开始时间
            throw new ServiceException(ResultCode.EXAM_START_TIME_AFTER_CURRENT_TIME);
        }
    }

    private void checkExam(Exam exam) {
        //        开始时间在当前时间之前
        if(exam.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_STARTED);
        }
    }
}
